package controllers

import (
	"gouniapp/configs"
	"context"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"math"
	"time"
	"encoding/json"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/payment/notify/request"
	request2 "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/order/request"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
	"github.com/gin-gonic/gin"
)

// 统一下单请求参数
type OrderRequest struct {
	Code   string  `json:"code"`   // 小程序 code
	Amount float64 `json:"amount" binding:"required"` // 充值金额（元）
	Points int     `json:"points" binding:"required"` // 充值积分
}

// 统一下单接口
func CreateOrder(c *gin.Context) {
	// 1. 用户鉴权
	currentUser := GetCurrentUser(c)
	if currentUser == nil {
		JsonResponse(c, http.StatusUnauthorized, "请先登录", nil)
		return
	}
	// 2. 参数校验
	var req OrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		JsonResponse(c, http.StatusBadRequest, "参数错误: "+err.Error(), nil)
		return
	}
	// 3. 获取 OpenID
	openID := currentUser.OpenID
	if openID == "" {
	    if req.Code == "" {
	        JsonResponse(c, http.StatusBadRequest, "code为空", nil)
    		return
	    }
    	session, err := configs.MiniProgramApp.Auth.Session(c.Request.Context(), req.Code)
    	if err != nil || session.OpenID == "" {
    		JsonResponse(c, http.StatusBadRequest, "获取用户身份失败", nil)
    		return
    	}
    	openID = session.OpenID
        // 更新OpenID
    	if err := configs.DB.Model(&currentUser).Update("open_id", openID).Error; err != nil {
    		JsonResponse(c, http.StatusInternalServerError, "openid更新失败", nil)
    		return
    	}
	}
	// 4. 生成订单号（示例，实际需确保唯一性）
	orderNo := fmt.Sprintf("%d%d", time.Now().UnixNano(), currentUser.ID)
	// 5. 创建本地订单（防止重复支付）
	order := configs.Order{
		UserID:     currentUser.ID,
		OrderNo:    orderNo,
		Amount:     req.Amount,
		Points:     req.Points,
		Status:     configs.OrderStatusPending,
		OpenID:     openID,
	}
	if err := configs.DB.Create(&order).Error; err != nil {
		log.Printf("创建订单失败: %v", err)
		JsonResponse(c, http.StatusInternalServerError, "创建订单失败", nil)
		return
	}
	// 6. 调用微信支付
	paymentApp := configs.PaymentApps["miniProgram"]
	if paymentApp == nil {
		JsonResponse(c, http.StatusInternalServerError, "支付服务未就绪", nil)
		return
	}
	prepayReq := &request2.RequestJSAPIPrepay{
		Amount: &request2.JSAPIAmount{
			Total: int(math.Round(req.Amount * 100)), // 单位：分
			Currency: "CNY",
		},
		Description: fmt.Sprintf("积分充值 %d 点", req.Points),
		OutTradeNo:  orderNo,
		Payer: &request2.JSAPIPayer{
			OpenID: openID,
		},
		Attach: strconv.Itoa(req.Points), // 传递积分
	}
	ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
	defer cancel()
	prepayResp, err := paymentApp.Order.JSAPITransaction(ctx, prepayReq)
	if err != nil {
		JsonResponse(c, http.StatusInternalServerError, "支付请求失败: "+err.Error(), nil)
		return
	}
	// 7. 返回支付参数
	payConfig, err := paymentApp.JSSDK.BridgeConfig(prepayResp.PrepayID, true) //// 根据prepay_id, 拼接出jssdk所需要的config
	if err != nil {
		JsonResponse(c, http.StatusInternalServerError, "生成支付配置失败", nil)
		return
	}
	payConfBytes, ok := payConfig.([]byte)
	if !ok {
		JsonResponse(c, http.StatusInternalServerError, "类型断言失败", nil)
		return
	}
	var payConfObj map[string]interface{}
	err = json.Unmarshal(payConfBytes, &payConfObj)
	if err != nil {
	    JsonResponse(c, http.StatusInternalServerError, err.Error(), nil)
		return
	}
	if payConfObj["package"] == "prepay_id=" {
	    JsonResponse(c, http.StatusInternalServerError, "参数错误，检查appid和商户号", payConfObj)
	    return
	}
	payConfObj["orderId"] = order.ID
	JsonResponse(c, http.StatusOK, "下单成功", payConfObj)
}

// 支付回调处理
func CallbackWXNotify(c *gin.Context) {
	paymentApp := configs.PaymentApps["miniProgram"]
	if paymentApp == nil {
		c.String(http.StatusInternalServerError, "支付实例未初始化")
		return
	}
	notifyRes, err := paymentApp.HandlePaidNotify(
		c.Request,
		func(message *request.RequestNotify, transaction *models.Transaction, fail func(message string)) interface{} {
			// 1. 校验支付状态
			if message.EventType != "TRANSACTION.SUCCESS" {
				log.Println("支付未成功事件类型:", message.EventType)
				return true // 非成功事件直接确认处理
			}
			// 2. 验证交易信息
			if transaction == nil || transaction.OutTradeNo == "" {
				fail("无效的交易信息")
				return nil
			}
			// 3. 查询本地订单
			order, err := configs.GetOrderByNo(transaction.OutTradeNo)
			if err != nil {
				fail("订单不存在: " + transaction.OutTradeNo)
				return nil
			}
			// 4. 幂等性检查
			if order.Status != configs.OrderStatusPending {
				log.Printf("订单 %s 已处理", transaction.OutTradeNo)
				return true
			}
			// 5. 解析附加参数
			points, _ := strconv.Atoi(transaction.Attach)
			if points <= 0 {
				fail("无效积分参数: " + transaction.Attach)
				return nil
			}
			//金额比对
			expectedTotal := int64(math.Round(order.Amount * 100))
            if transaction.Amount.Total != expectedTotal {
                fail(fmt.Sprintf("金额校验失败 订单:%d分 实际:%d分", expectedTotal, transaction.Amount.Total))
                return nil
            }
            if points != order.Points {
                fail(fmt.Sprintf("积分校验失败 订单:%d 回调:%d", order.Points, points))
                return nil
            }
			// 6. 数据库事务
			tx := configs.DB.Begin()
			defer tx.Rollback()
			// 更新用户积分
			if err := configs.AddUserPoints(tx, order.UserID, points); err != nil {
				fail("积分更新失败: " + err.Error())
				return nil
			}
			// 更新订单状态
			if err := tx.Model(&order).Update("status", configs.OrderStatusSuccess).Error; err != nil {
				fail("订单状态更新失败: " + err.Error())
				return nil
			}
            userCacheKey := fmt.Sprintf("user:%d", order.UserID)  //删除缓存
            configs.CRdb.Delete(userCacheKey)
			tx.Commit()
            msg := fmt.Sprintf("充值成功，订单号%s，充值获得%d积分，支付金额：%.2f，订单创建时间：%s", order.OrderNo, points, order.Amount/100, order.CreatedAt.Format("2006-01-02 15:04:05"))
            go configs.CreateMessage(nil, order.UserID, "充值成功", msg)
			return true // 必须返回true确认处理成功
		},
	)
	if err != nil {
		log.Printf("回调处理异常: %v", err)
		c.String(http.StatusBadRequest, "回调解析失败")
		return
	}
	if err := notifyRes.Write(c.Writer); err != nil {
		log.Printf("回调响应失败: %v", err)
	}
}

// 轮询判断支付结果
func IspaySuccess(c *gin.Context) {
    type Payjg struct {
    	OrderId     uint    `json:"orderId"`
    }
	var jsons Payjg
	if err := c.ShouldBindJSON(&jsons); err != nil {
	    JsonResponse(c, http.StatusInternalServerError, "异步判断支付结果json错误", nil)
		return
	}
    if jsons.OrderId == 0 {
        JsonResponse(c, http.StatusInternalServerError, "orderId为空", nil)
        return
    }
	// 查询本地订单
	order, err := configs.GetOrderByID(jsons.OrderId)
	if err != nil {
	    JsonResponse(c, http.StatusInternalServerError, "订单不存在", jsons.OrderId)
		return
	}
	currentUser := GetCurrentUser(c)
	if currentUser == nil {
		JsonResponse(c, http.StatusUnauthorized, "请重新登录", nil)
		return
	}
    if order.UserID != currentUser.ID {
        JsonResponse(c, http.StatusForbidden, "无权访问此订单", nil)
        return
    }
	if order.Status == configs.OrderStatusSuccess {
	    JsonResponse(c, http.StatusOK, "success", "success")
	} else {
	    JsonResponse(c, http.StatusOK, "success", "pending")
	}
}